﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Magiq.Delete;
using Magiq.Insert;
using Magiq.Tests.Support;
using Magiq.Update;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Constraints;

namespace Magiq.Tests {
  [TestFixture]
  public class ConfigurationTests {
    #region Setup/Teardown

    [SetUp]
    public void SetUp() {
      Magiq.Clear();
      items = new TestQueryable(new List<Model>
                                  {
                                    new Model("a"),
                                    new Model("b"),
                                    new Model("c")
                                  });
    }

    #endregion

    private TestQueryable items;

    private static IMagiqProvider RegisterMockMagiq() {
      var magiq = MockRepository.GenerateMock<IMagiqProvider>();
      magiq.Expect(x => x.GetUpdateProvider()).Return(MockRepository.GenerateMock<IUpdateProvider>()).Repeat.Any();
      magiq.Expect(x => x.GetInsertProvider()).Return(MockRepository.GenerateMock<IInsertProvider>()).Repeat.Any();
      magiq.Expect(x => x.GetDeleteProvider()).Return(MockRepository.GenerateMock<IDeleteProvider>()).Repeat.Any();
      Magiq.Register(magiq);
      return magiq;
    }

    private class Model {
      public Model(string prop1) {
        Prop1 = prop1;
      }

      public string Prop1 { get; set; }
    }

    private class TestQueryable : IQueryable<Model> {
      private readonly List<Model> list;

      public TestQueryable(List<Model> list) {
        this.list = list;
      }

      public bool Enumerated { get; private set; }

      #region IQueryable<Model> Members

      public IEnumerator<Model> GetEnumerator() {
        Enumerated = true;
        return list.GetEnumerator();
      }

      IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
      }

      public Expression Expression {
        get { throw new NotImplementedException(); }
      }

      public Type ElementType {
        get { throw new NotImplementedException(); }
      }

      public IQueryProvider Provider {
        get { throw new NotImplementedException(); }
      }

      #endregion
    }

    [Test]
    public void ShouldCallMagiqToObjectsIfNoRegisteredMagiqHandlesTheQueryable() {
      var magiq = RegisterMockMagiq();
      magiq.Expect(x => x.Handles(null)).IgnoreArguments().Return(false).Repeat.Any();

      items.Set(x => x.Prop1, "lala").Update();
      items.Enumerated.Should(Be.True);
    }

    [Test]
    public void ShouldNotUseMagiqsThatDoesntHandlesTheQueryable() {
      var magiq = RegisterMockMagiq();
      magiq.Expect(x => x.Handles(null)).IgnoreArguments().Return(false).Repeat.Any();
      var magiq2 = RegisterMockMagiq();
      magiq2.Expect(x => x.Handles(null)).Constraints(Is.TypeOf<TestQueryable>()).Return(true).Repeat.Once();
      magiq2.GetUpdateProvider().Expect(x => x.Execute<Model>(null)).IgnoreArguments()
        .Callback((IQueryableUpdate<Model> x) => x.Where is TestQueryable).Return(2).Repeat.Once();

      items.Set(x => x.Prop1, "lala").Update().Should(Be.EqualTo(2));
    }

    [Test]
    public void ShouldUseConfiguredMagiqs() {
      var magiq = RegisterMockMagiq();
      magiq.Expect(x => x.Handles(null)).Constraints(Is.TypeOf<TestQueryable>()).Return(true).Repeat.Once();
      magiq.GetUpdateProvider().Expect(x => x.Execute<Model>(null)).IgnoreArguments()
        .Callback((IQueryableUpdate<Model> x) => x.Where is TestQueryable).Return(2).Repeat.Once();

      items.Set(x => x.Prop1, "lala").Update().Should(Be.EqualTo(2));
    }
  }
}